use crate::core::AppState;
use crate::db::open_db;
use rusqlite::{params, OptionalExtension};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use tauri::State;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AiTimelineChangeLine {
    pub line: i64,
    pub before: String,
    pub after: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AiTimelineEntry {
    pub id: String,
    pub ts: i64,
    pub kind: String,
    pub path: String,
    pub tool: Option<String>,
    pub replacements: Option<i64>,
    pub changes: Option<Vec<AiTimelineChangeLine>>,
    pub note: Option<String>,
    pub beforeContent: Option<String>,
    pub afterContent: Option<String>,
    pub fileSize: Option<i64>,
    pub isDirectory: Option<bool>,
}

fn ensure_table(db_path: &PathBuf) -> Result<(), String> {
    let conn = open_db(db_path).map_err(|e| e.to_string())?;
    conn.execute(
        "CREATE TABLE IF NOT EXISTS ai_timeline (
            workspace TEXT NOT NULL,
            session_id TEXT NULL,
            id TEXT PRIMARY KEY,
            ts INTEGER NOT NULL,
            kind TEXT NOT NULL,
            path TEXT NOT NULL,
            tool TEXT NULL,
            replacements INTEGER NULL,
            changes TEXT NULL,
            note TEXT NULL,
            beforeContent TEXT NULL,
            afterContent TEXT NULL,
            fileSize INTEGER NULL,
            isDirectory INTEGER NULL
        )",
        [],
    )
    .map_err(|e| e.to_string())?;

    // Migration: add session_id column if it does not exist (ignore error if already exists)
    let _ = conn.execute("ALTER TABLE ai_timeline ADD COLUMN session_id TEXT NULL", []);

    Ok(())
}

#[tauri::command]
pub fn ai_timeline_add(state: State<AppState>, workspace: String, session_id: String, entry: AiTimelineEntry) -> Result<(), String> {
    ensure_table(&state.db_path)?;
    let conn = open_db(&state.db_path).map_err(|e| e.to_string())?;
    let changes_json = if let Some(v) = &entry.changes { serde_json::to_string(v).ok() } else { None };
    conn.execute(
        "INSERT INTO ai_timeline (workspace, session_id, id, ts, kind, path, tool, replacements, changes, note, beforeContent, afterContent, fileSize, isDirectory)
         VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14)
         ON CONFLICT(id) DO UPDATE SET
           workspace=excluded.workspace,
           session_id=excluded.session_id,
           ts=excluded.ts,
           kind=excluded.kind,
           path=excluded.path,
           tool=excluded.tool,
           replacements=excluded.replacements,
           changes=excluded.changes,
           note=excluded.note,
           beforeContent=excluded.beforeContent,
           afterContent=excluded.afterContent,
           fileSize=excluded.fileSize,
           isDirectory=excluded.isDirectory",
        params![
            workspace,
            session_id,
            entry.id,
            entry.ts,
            entry.kind,
            entry.path,
            entry.tool,
            entry.replacements,
            changes_json,
            entry.note,
            entry.beforeContent,
            entry.afterContent,
            entry.fileSize,
            entry.isDirectory.map(|b| if b { 1 } else { 0 })
        ],
    )
    .map_err(|e| e.to_string())?;
    Ok(())
}

#[tauri::command]
pub fn ai_timeline_list(state: State<AppState>, workspace: String, session_id: String) -> Result<Vec<AiTimelineEntry>, String> {
    ensure_table(&state.db_path)?;
    let conn = open_db(&state.db_path).map_err(|e| e.to_string())?;
    let mut stmt = conn
        .prepare(
            "SELECT id, ts, kind, path, tool, replacements, changes, note, beforeContent, afterContent, fileSize, isDirectory
             FROM ai_timeline WHERE workspace=?1 AND session_id=?2 ORDER BY ts DESC",
        )
        .map_err(|e| e.to_string())?;
    let rows = stmt
        .query_map(params![workspace, session_id], |row| {
            let changes_str: Option<String> = row.get(6)?;
            let changes: Option<Vec<AiTimelineChangeLine>> = match changes_str {
                Some(s) => serde_json::from_str(&s).ok(),
                None => None,
            };
            let is_dir_i: Option<i64> = row.get(11)?;
            Ok(AiTimelineEntry {
                id: row.get(0)?,
                ts: row.get(1)?,
                kind: row.get(2)?,
                path: row.get(3)?,
                tool: row.get(4)?,
                replacements: row.get(5)?,
                changes,
                note: row.get(7)?,
                beforeContent: row.get(8)?,
                afterContent: row.get(9)?,
                fileSize: row.get(10)?,
                isDirectory: is_dir_i.map(|v| v != 0),
            })
        })
        .map_err(|e| e.to_string())?;
    let mut out = Vec::new();
    for r in rows { out.push(r.map_err(|e| e.to_string())?); }
    Ok(out)
}

#[tauri::command]
pub fn ai_timeline_clear(state: State<AppState>, workspace: String, session_id: String) -> Result<(), String> {
    ensure_table(&state.db_path)?;
    let conn = open_db(&state.db_path).map_err(|e| e.to_string())?;
    conn.execute("DELETE FROM ai_timeline WHERE workspace=?1 AND session_id=?2", params![workspace, session_id])
        .map_err(|e| e.to_string())?;
    Ok(())
}

#[tauri::command]
pub fn ai_timeline_clear_from(state: State<AppState>, workspace: String, session_id: String, from_timestamp: i64) -> Result<(), String> {
    ensure_table(&state.db_path)?;
    let conn = open_db(&state.db_path).map_err(|e| e.to_string())?;
    conn.execute("DELETE FROM ai_timeline WHERE workspace=?1 AND session_id=?2 AND ts>=?3", params![workspace, session_id, from_timestamp])
        .map_err(|e| e.to_string())?;
    Ok(())
}